﻿using System;
using System.Linq;
using Belphegor.Composites;
using Belphegor.Dynamics;
using Belphegor.Helpers;
using Zeta;
using Zeta.Common.Helpers;
using Zeta.CommonBot;
using Zeta.Internals.Actors;
using Zeta.TreeSharp;
using Action = Zeta.TreeSharp.Action;

namespace Belphegor.Routines
{
    public class Wizard
    {
        [Class(ActorClass.Wizard)]
        [Behavior(BehaviorType.Buff)]
        public static Composite WizardBuffs()
        {
            return new PrioritySelector(
                Common.CreateWaitForAttack(),
                //new SelfCast(SNOPower.Wizard_MagicWeapon, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_MagicWeapon)),

                // Familiar doesn't have a visible buff.
                new Decorator(ret => _familiarTimer.IsFinished && PowerManager.CanCast(SNOPower.Wizard_Familiar),
                    new Sequence(
                        new SelfCast(SNOPower.Wizard_Familiar),
                        new Action(ret => _familiarTimer.Reset())))
                //new SelfCast(SNOPower.Wizard_EnergyArmor, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_EnergyArmor)),
                //new SelfCast(SNOPower.Wizard_StormArmor, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_StormArmor)),
                //new SelfCast(SNOPower.Wizard_IceArmor, extra => !ZetaDia.Me.HasBuff(SNOPower.Wizard_IceArmor))
                );
        }
      

        [Class(ActorClass.Wizard)]
        [Behavior(BehaviorType.Combat)]
        public static Composite WizardCombat()
        {
            return
                new PrioritySelector(ctx => CombatTargeting.Instance.FirstNpc,
                    new Decorator(ctx => ctx != null,
                    new PrioritySelector(

                    Common.CreateWaitWhileIncapacitated(),
                    Common.CreateWaitForAttack(),
                    Common.CreateUsePotion(),

                    // Make sure we are within range/line of sight of the unit.
                    Movement.MoveTo(ctx => ((DiaUnit)ctx).Position, 50f),
                    //Movement.MoveToLineOfSight(ctx => ((DiaUnit)ctx)),

                    new Decorator(ret => ZetaDia.Me.HasBuff(SNOPower.Wizard_Archon),
                        new PrioritySelector(
                            new SelfCast(SNOPower.Wizard_Archon_SlowTime, extra => ZetaDia.Me.HitpointsCurrentPct <= 0.4),
                            new SelfCast(SNOPower.Wizard_Archon_ArcaneBlast, ctx => (((DiaUnit)ctx).Distance < 16f && Common.IsElite((DiaUnit)ctx)) 
                                || Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 2),
                            new CastOnUnit(SNOPower.Wizard_Archon_DisintegrationWave, ctx => ((DiaUnit)ctx).ACDGuid),
                            new CastOnUnit(SNOPower.Wizard_Archon_ArcaneStrike, ctx => ((DiaUnit)ctx).ACDGuid)
                            )),

                    // Low health stuff
                    new SelfCast(SNOPower.Wizard_DiamondSkin, extra => ZetaDia.Me.HitpointsCurrentPct <= 0.6),
                    new SelfCast(SNOPower.Wizard_MirrorImage, extra => ZetaDia.Me.HitpointsCurrentPct <= 0.5),
                    new SelfCast(SNOPower.Wizard_SlowTime, extra => ZetaDia.Me.HitpointsCurrentPct <= 0.4),


                    // AoE spells.
                    new SelfCast(SNOPower.Wizard_WaveOfForce,
                        ctx => Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 12f) >= 2 ||
                        (Common.IsElite((DiaUnit)ctx) && ((DiaUnit)ctx).Distance < 12f)),

                    new SelfCast(SNOPower.Wizard_FrostNova, 
                        ctx => Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >=2 ||
                                (Common.IsElite((DiaUnit)ctx) && ((DiaUnit)ctx).Distance < 16f)),

                    //Hydra
                    new Decorator(ret => _hydraTimer.IsFinished && PowerManager.CanCast(SNOPower.Wizard_Hydra),
                        new Sequence(
                            new CastAtLocation(SNOPower.Wizard_Hydra, ctx => ((DiaUnit)ctx).Position, ctx => !HasHydra
                                && Common.IsElite((DiaUnit)ctx) || Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 12f) >= 4),
                            new Action(ret => _hydraTimer.Reset()))),

                    new SelfCast(SNOPower.Wizard_Archon, ctx => Common.IsElite((DiaUnit)ctx) ||
                        Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 60f) >= 5),

                    new CastOnUnit(SNOPower.Wizard_EnergyTwister, ctx => ((DiaUnit)ctx).ACDGuid,
                        ctx => Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 20f) >= 4),


                    new Decorator(ret => _meteorTimer.IsFinished && PowerManager.CanCast(SNOPower.Wizard_Meteor),
                        new Sequence(
                        new CastAtLocation(SNOPower.Wizard_Meteor, ctx => ((DiaUnit)ctx).Position, 
                            ctx => Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 4 ||
                                    Common.IsElite((DiaUnit)ctx)),
                        new Action(ret => _meteorTimer.Reset()))),

                    new Decorator(ret => _blizzardTimer.IsFinished && PowerManager.CanCast(SNOPower.Wizard_Blizzard),
                        new Sequence(
                            new CastAtLocation(SNOPower.Wizard_Blizzard, ctx => ((DiaUnit)ctx).Position, 
                                ctx => Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 16f) >= 4),
                            new Action(ret => _blizzardTimer.Reset()))),

                   new Decorator(ret => _explosiveBlast.IsFinished && PowerManager.CanCast(SNOPower.Wizard_ExplosiveBlast),
                        new Sequence(
                            new SelfCast(SNOPower.Wizard_ExplosiveBlast,
                                ctx => Clusters.GetClusterCount(ZetaDia.Me, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 12f) >= 2 ||
                                        (Common.IsElite((DiaUnit)ctx) && ((DiaUnit)ctx).Distance < 12f)),
                            new Action(ret => _explosiveBlast.Reset()))),

                    // Arcane power spenders.
                    new CastOnUnit(SNOPower.Wizard_ArcaneOrb, ctx => ((DiaUnit)ctx).ACDGuid,
                        ctx => Clusters.GetClusterCount((DiaUnit)ctx, CombatTargeting.Instance.LastObjects, ClusterType.Radius, 20f) >= 2),
                    //new CastOnUnit(SNOPower.Wizard_ArcaneOrb, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_RayOfFrost, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_ArcaneTorrent, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_Disintegrate, ctx => ((DiaUnit)ctx).ACDGuid),

                    // Signature spells.
                    new CastOnUnit(SNOPower.Wizard_SpectralBlade, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_Electrocute, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_ShockPulse, ctx => ((DiaUnit)ctx).ACDGuid),
                    new CastOnUnit(SNOPower.Wizard_MagicMissile, ctx => ((DiaUnit)ctx).ACDGuid)
                    )),

                   new Action(ret => RunStatus.Success)
                   );
        }

        #region Timers
        static Wizard()
        {
            GameEvents.OnGameLeft += ResetTimers;
            GameEvents.OnPlayerDied += ResetTimers;
        }

        static void ResetTimers(object sender, EventArgs e)
        {
            _familiarTimer.Stop();
            _blizzardTimer.Stop();
            _hydraTimer.Stop();
            _meteorTimer.Stop();
            _explosiveBlast.Stop();
        }

        private static WaitTimer _explosiveBlast = new WaitTimer(TimeSpan.FromSeconds(5));
        private static WaitTimer _blizzardTimer = new WaitTimer(TimeSpan.FromSeconds(5));
        private static WaitTimer _meteorTimer = new WaitTimer(TimeSpan.FromSeconds(5));
        private static WaitTimer _familiarTimer = new WaitTimer(TimeSpan.FromMinutes(5));
        private static WaitTimer _hydraTimer = new WaitTimer(TimeSpan.FromSeconds(10));

        #endregion

        public static bool HasHydra
        {
            get
            {
                int dynId = ZetaDia.Me.CommonData.DynamicId;
                var currentTarget = CombatTargeting.Instance.FirstNpc;
                var summoned = ZetaDia.Actors.GetActorsOfType<DiaUnit>().FirstOrDefault(u => u.SummonedByACDId == dynId && u.Name.StartsWith("Wizard_Hydra") && u.Position.Distance(currentTarget.Position) < 30f);
                return summoned != null && summoned.CommonData != null && currentTarget.CommonData != null;
            }
        }

        public static void WizardOnLevelUp(object sender, EventArgs e)
        {
            if (ZetaDia.Me.ActorClass != ActorClass.Wizard)
                return;

            int myLevel = ZetaDia.Me.Level;

            Logger.Write("Player leveled up, congrats! Your level is now: {0}",
                myLevel
                );


            // ********** PRIMARY SLOT CHANGES **********
            // Set Shock Pulse as primary.
            if (myLevel == 3)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ShockPulse, -1, 0);
                Logger.Write("Setting Shock Pulse as Primary");
            }
            // Set Shock Pulse-Explosive bolts as primary.
            if (myLevel == 9)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ShockPulse, 1, 0);
                Logger.Write("Changing rune for Shock Pulse: \"Explosive Bolts\"");
            }
            // Set Electrocute as primary.
            if (myLevel == 15)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Electrocute, -1, 0);
                Logger.Write("Setting Electrocute as Primary");
            }
            // Set Electrocute-Chain lightning as primary.
            if (myLevel == 22)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Electrocute, 1, 0);
                Logger.Write("Changing rune for Electrocute: \"Chain Lightning\"");
            }

            // ********** SECONDARY SLOT CHANGES **********
            // Set Ray of Frost as secondary spell.
            if (myLevel == 2)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_RayOfFrost, -1, 1);
                Logger.Write("Setting Ray of Frost as Secondary");
            }
            // Set arcane orb as secondary
            if (myLevel == 5)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ArcaneOrb, -1, 1);
                Logger.Write("Setting Arcane Orb as Secondary");
            }
            // Set arcane orb rune to "obliteration"
            if (myLevel == 11)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ArcaneOrb, 1, 1);
                Logger.Write("Changing rune for Arcane Orb: \"Obliteration\"");
            }
            // ********** SKILL SLOTS 1-4 **********
            // Set Frost Nova as slot 1
            if (myLevel == 4)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_FrostNova, -1, 2);
                Logger.Write("Setting Frost Nova as slot 1");
            }
            // Set Diamond Skin as slot 1
            if (myLevel == 8)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_DiamondSkin, -1, 2);
                Logger.Write("Setting Diamond Skin as slot 1");
            }
            // Level 9, slot 2 unlocked!
            // Set Wave of Force as slot 2
            if (myLevel == 9)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_WaveOfForce, -1, 3);
                Logger.Write("Setting Wave of Force as slot 2");
            }
            // Level 14, slot 3 unlocked!
            // Set Diamond Skin-Crystal Shell as slot 1, Ice Armor as slot 3
            if (myLevel == 14)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_DiamondSkin, 1, 2);
                Logger.Write("Changing rune for Diamond Skin: \"Crystal Shell\"");
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_IceArmor, -1, 4);
                Logger.Write("Setting Ice Armor as slot 3");
            }
            // Set Wave of Force-Impactful Wave as slot 2
            if (myLevel == 15)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_WaveOfForce, 1, 3);
                Logger.Write("Changing rune for Wave of Force: \"Impactful Wave\"");
            }
            // Level 19, slot 4 unlocked!
            // Set Explosive Blast as slot 4
            if (myLevel == 19)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_ExplosiveBlast, -1, 5);
                Logger.Write("Setting Explosive Blast as slot 4");
            }
            // Set Ice Armor-Chilling Aura as slot 3, Hydra as slot 4
            if (myLevel == 21)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_IceArmor, 1, 4);
                Logger.Write("Changing rune for Ice Armor: \"Chilling Aura\"");
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Hydra, -1, 5);
                Logger.Write("Setting Hydra as slot 4");
            }
            // Set Hydra-Arcane Hydra as slot 4
            if (myLevel == 26)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Hydra, 1, 5);
                Logger.Write("Changing rune for Hydra: \"Arcane Hydra\"");
            }
            // Set Energy Armor as slot 3
            if (myLevel == 28)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_EnergyArmor, -1, 4);
                Logger.Write("Setting Energy Armor as slot 3");
            }
            // Set Energy Armor-Absorption as slot 3
            if (myLevel == 32)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_EnergyArmor, 1, 4);
                Logger.Write("Changing rune for Energy Armor: \"Absorption\"");
            }
            // Set Hydra-Venom Hydra as slot 4
            if (myLevel == 38)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_Hydra, 3, 5);
                Logger.Write("Changing rune for Hydra: \"Venom Hydra\"");
            }
            // Set Energy Armor-Pinpoint Barrier as slot 3
            if (myLevel == 41)
            {
                ZetaDia.Me.SetActiveSkill(SNOPower.Wizard_EnergyArmor, 2, 4);
                Logger.Write("Changing rune for Energy Armor: \"Pinpoint Barrier\"");
            }

            // ********** PASSIVE SKILLS **********
            if (myLevel == 10)
            {
                // Blur - Decreases melee damage taken by 20%.
                ZetaDia.Me.SetTraits(SNOPower.Wizard_Passive_Blur);
            }
            if (myLevel == 20)
            {
                // Blur - Decreases melee damage taken by 20%.
                // Prodigy - 4 arcane power from signature casts
                ZetaDia.Me.SetTraits(SNOPower.Wizard_Passive_Blur, SNOPower.Wizard_Passive_Prodigy);
            }
            if (myLevel == 30)
            {
                // Blur - Decreases melee damage taken by 20%.
                // Prodigy - 4 arcane power from signature casts
                // Astral Presence - +20 arcane power, +2 arcane regen
                ZetaDia.Me.SetTraits(SNOPower.Wizard_Passive_Blur, SNOPower.Wizard_Passive_Prodigy, SNOPower.Wizard_Passive_AstralPresence);
            }

        }
    }
}
